package com.mvc.interceptor;

import com.mvc.annotation.RequestMapping;
import com.mvc.annotation.Security;
import com.mvc.model.MappedHandler;
import com.mvc.model.Result;
import com.mvc.util.JsonUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Kirito
 * @description: PermissionInterceptor --> 权限拦截器
 * @date 2020/04/09
 */
public class PermissionInterceptor implements Interceptor {

    private static final String ADMIN_PERMISSION = "admin";
    private static final String MEMBER_PERMISSION = "vip";
    private static final String GUEST_PERMISSION = "guest";

    private static final String ADMIN_USER = "admin";
    private static final String MEMBER_USER = "lisi";
    private static final String GUEST_USER = "zhangsan";

    private static Map<String, String> permissions;

    /**
     * 初始化一些默认权限设置
     */
    static {
        permissions = new HashMap<>(3);
        permissions.put(ADMIN_USER, ADMIN_PERMISSION);
        permissions.put(MEMBER_USER, MEMBER_PERMISSION);
        permissions.put(GUEST_USER, GUEST_PERMISSION);
    }

    /**
     * TODO: @Security注解当前仅支持配在类上或方法上，后续应该两者都支持，并且支持设置与或关系
     *
     * @param req     HttpServletRequest
     * @param resp    HttpServletResponse
     * @param handler Object
     * @return boolean
     * @throws Exception Exception
     */
    @Override
    public boolean preHandler(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        String userName = req.getParameter("username");
        String uri = req.getRequestURI();
        MappedHandler mappedHandler = (MappedHandler) handler;
        Class<?> clazz = mappedHandler.getHandler().getClass();
        RequestMapping mapping = clazz.getAnnotation(RequestMapping.class);

        if (clazz.isAnnotationPresent(Security.class)) {
            // 如果类上接了权限注解，那么直接进行权限校验
            Security security = clazz.getAnnotation(Security.class);
            boolean success = checkPermissions(security.value(), userName);
            if (!success) {
                // 校验失败，返回权限不足信息
                response(userName, resp);
            }
            return success;
        } else {
            // 如果handler类上没有@Security注解，那么看方法上有没有
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                // 如果不是uri对应的方法也跳过
                StringBuilder sb = new StringBuilder(mapping.value());
                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                if (null != requestMapping) {
                    sb.append(requestMapping.value());
                }
                if (!uri.equals(sb.toString())) continue;

                // 如果该方法上没有添加权限注解，直接放行
                if (!method.isAnnotationPresent(Security.class)) return true;

                Security security = method.getAnnotation(Security.class);
                boolean success = checkPermissions(security.value(), userName);
                if (!success) {
                    response(userName, resp);
                }
                return success;
            }

        }

        // 均不满足的情况下放行
        return true;
    }

    private void response(String userName, HttpServletResponse resp) throws IOException {
        Result result = new Result();
        result.setStatus("403");
        result.setMessage("Sorry to " + userName + ", You don't have permission.");
        resp.getWriter().print(JsonUtils.object2Json(result));
    }

    private static boolean checkPermissions(String[] value, String userName) {
        String permission = permissions.get(userName);
        return Arrays.asList(value).contains(permission);
    }
}
